#pragma once
#include <iostream>
#include <sys/select.h>
#include "Socket.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"


using namespace hcc;

class SelectServer
{
    const static int gnum=sizeof(fd_set)*8;
    const static int gdefaultfd=-1;

    public:
    SelectServer(uint16_t port):_port(port),_listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
    }
    void InitServer()
    {
        for(int i=0;i<gnum;i++)
        {
            fd_array[i]=gdefaultfd;
        }
        fd_array[0] = _listensock->Sockfd(); // 默认直接添加listensock到数组中
    }

    void HandlerIO(int i)
    {
        //普通的我呢见描述符 ， 正常的读写
        char buffer[1024];
        ssize_t n=::recv(fd_array[i],buffer,sizeof(buffer)-1,0);//这里的读取不会阻塞，因为select 是等待资源就绪后才执行这个的
        if(n>0)
        {
            buffer[n]=0;
            std::cout<<"client say#: "<<buffer<<std::endl;
             std::string content = "<html><body><h1>hello bite</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;

            ::send(fd_array[i],echo_str.c_str(),echo_str.size(),0);
        }
        else if(n==0)
        {
            LOG(INFO,"client quit ... \n");
            //读取完毕，关闭fd
            ::close(fd_array[i]);

            //select 不需要在关心该fd了
            fd_array[i]=gdefaultfd;
        }
        else 
        {
            LOG(ERROR,"recv error\n");
            ::close(fd_array[i]);
            fd_array[i]=gdefaultfd;
        }
    }
    
    //处理新连接
    void Accepter()
    {
        InetAddr addr;
        int sockfd=_listensock->Accepter(&addr);
        if(sockfd>0)
        {
            LOG(DEBUG,"get a new link, client info : %s : %d\n",addr.Ip().c_str(),addr.Port());
            // 获取了连接，但是我们不能读取，因为条件不一定满足
            // 怎样知道fd的数据是否就绪了呢 通过select
            // 想办法添加给select 由select统一进行监管 
            // 只要将新的fd 添加到fd_array 数组中即可
            bool flag=false;
            for(int pos=1;pos<gnum;pos++)
            {
                if(fd_array[pos]==gdefaultfd)
                {
                    flag=true;
                    fd_array[pos]=sockfd;
                    LOG(INFO,"add %d to fd_array success ! \n",sockfd);
                    break;
                }
            }
            if(!flag)
            {
                LOG(WARNING,"Server is full \n");
                ::close(sockfd);
            }
        }

    }

    //一定会存在大量的fd就绪，可能是普通sockfd，也可能是listensockfd
    void HandlerEvent(fd_set &rfds)
    {
        //事件派发
        for(int i=0;i<gnum;i++)
        {
            if(fd_array[i]==gdefaultfd)
            continue;
            if(FD_ISSET(fd_array[i],&rfds))
            {
                //读事件就绪 
                //1. listensockfd 2. normal sockfd
                if(_listensock->Sockfd()==fd_array[i])
                {
                    Accepter();
                    
                }
                else 
                {
                    HandlerIO(i);
                }
            }
        }
    }

    void Loop()
    {
        LOG(INFO,"开始循环\n");
        while(true)
        {
            //1. 文件描述符进行初始化
            fd_set rfds;
            FD_ZERO(&rfds);

            int max_fd=gdefaultfd;

            //2. 合法的fd添加到rfds集合中
            for(int i=0;i<gnum;i++)
            {
                if(fd_array[i]==gdefaultfd)
                continue;
                FD_SET(fd_array[i],&rfds);

                //2.1 更新出最大的文件fd的值
                if(max_fd<fd_array[i]);
                {
                    max_fd=fd_array[i];
                }
            }
            struct timeval timeout={30,0};
            // _listensock->Accepter();//不能，listensock&&accept 我们也要看作IO类的函数，只管心新连接到来，等价于读事件就绪
            int n=::select(max_fd+1,&rfds,nullptr,nullptr,&timeout);
            switch(n)
            {
                case 0:
                LOG(DEBUG,"time out ,%d.%d\n",timeout.tv_sec,timeout.tv_usec);
                break;
                case -1:
                LOG(ERROR,"select error\n");
                break;
                default:
                LOG(INFO,"haved event ready,n: %d\n",n);//如果事件就绪，但是不处理，select会一直通知我，知道我处理了
                HandlerEvent(rfds);
                // PrintDebug();
                sleep(1);
                break;
            }
        }
    }
    private:
    uint16_t _port;
    std::unique_ptr<Socket>_listensock;

    //select 要正常工作，需要借助一个辅助数组，来保存所有合法fd
    int fd_array[gnum];
};